--[[
跨服模块业务逻辑总入口
接口列表：
handle		包处理总入口
enter		申请进入跨服
leave		申请离开跨服
--]]
 
local Config = require("Config")
local LuaMongo = _G.lua_mongo
local DB = require("common.DB")
local CommonDefine = require("common.CommonDefine")
local LogDefine = require("common.LogDefine")
local Log = require("common.Log")
local Lang = require("common.Lang")
local Msg = require("core.Msg")
local Map = require("core.Map")
local Human = require("core.Human")
local Broadcast = require("broadcast.Broadcast")
local RoleDBLogic = require("role.RoleDBLogic")
local MPacketDefine = require("middle.MPacketDefine")
local MiddleDefine = require("middle.MiddleDefine")
local MiddlePacket = require("middle.MiddlePacket")
local MiddleOpenDB = require("middle.MiddleOpenDB")
local FamilyManager = require("family.FamilyManager")
local RoleFFI = require("role.RoleFFI")
local MiddleActivity = require("middle.MiddleActivity")
local PfLogic = require("tencentApi.PfLogic")
local MailLogicFW = require("mail.MailLogicFW")
local MiddleCopySign = require("middle.MiddleCopySign")
local CopyDefine = require("copy.Define")
local MiddleFamilySign = require("middle.MiddleFamilySign")


FD_2_SVNINDEX = FD_2_SVNINDEX or {} -- fd和svrindex的映射关系 {svrIndex, openDay, avgZhandouli}

NEED_LOGIN_DATA = NEED_LOGIN_DATA or {} -- uuid -> {time, svrIndex}
ENABLE_MIDDLE = ENABLE_MIDDLE or true -- 跨服是否开放
MAX_COUNT = 1100						  -- 跨服最大人数上限

local fields = {
	middleFlag=1,
	account=1,
	name=1,
}

function getFDBySvrIndex(svrIndex)
	for fd, v in pairs(FD_2_SVNINDEX) do
		if v.svrIndex == svrIndex then
			return fd
		end
	end
end

function getSvrInfoBySvrIndex(svrIndex)
	for fd, v in pairs(FD_2_SVNINDEX) do
		if v.svrIndex == svrIndex then
			return v
		end
	end
end

function handle(fd)
	MiddlePacket.handle(fd)
end

function G2M_HELLO(fd, msg)
	FD_2_SVNINDEX[fd] = {svrIndex = msg.svrIndex, openDay = nil, avgZhandouli = nil}
	print("hello:", msg.svrIndex, " fd:", fd)
end

function G2M_DISCONNECT(fd)
	FD_2_SVNINDEX[fd] = nil
	print("middle disconnect fd:", fd)
end

-- 登入跨服
function G2M_LOGIN_MIDDLE(fd, msg)
	if not Config.IS_MIDDLE then return end
	
	local svrIndex = nil 
	local svrInfo = FD_2_SVNINDEX[fd]
	if svrInfo then svrIndex = svrInfo.svrIndex end
	if svrIndex == nil then return end
	
	local uuid = msg.db._id
	if uuid == nil or uuid == "" then return end

	local retCode = MiddleDefine.G2M_LOGIN_OK
	if svrIndex ~= msg.svrIndex then
		retCode = MiddleDefine.G2M_LOGIN_FAIL_INVALID -- 游戏服向跨服服请求登录失败 无效参数
	elseif Human.onlineUuid[uuid] and Human.onlineUuid[uuid].fd then
		retCode = MiddleDefine.G2M_LOGIN_FAIL_HAS_ACCOUNT -- 帐号已经在跨服游戏
	elseif ENABLE_MIDDLE == false then
		retCode = MiddleDefine.G2M_LOGIN_FAIL_WEIHU -- 维护中
	else
		local onlineCnt = 0
		for k, v in pairs(Human.onlineHuman) do
			onlineCnt = onlineCnt + 1
		end
		if onlineCnt >= MAX_COUNT then
			retCode = MiddleDefine.G2M_LOGIN_FAIL_MAX_COUNT -- 最大人数上限
		end
	end
	
	if MiddleDefine.G2M_LOGIN_OK == retCode then
		if NEED_LOGIN_DATA[uuid] == nil then 
			NEED_LOGIN_DATA[uuid] = {time = os.time(), svrIndex = svrIndex, actID = msg.actID, copyID = msg.teamCopyID}
		else
			NEED_LOGIN_DATA[uuid].time = os.time()
			NEED_LOGIN_DATA[uuid].svrIndex = svrIndex
			NEED_LOGIN_DATA[uuid].actID = msg.actID
			NEED_LOGIN_DATA[uuid].copyID = msg.teamCopyID
		end
	end
	
	_G.msg_middle_parse.writebegin(fd)
	_G.msg_middle_parse.writeint(MPacketDefine.M2G_LOGIN_MIDDLE_RET)
	_G.msg_middle_parse.writeint(svrIndex)
	_G.msg_middle_parse.writeint(retCode)
	_G.msg_middle_parse.writestring(msg.db._id)
	_G.msg_middle_parse.writeend()
end

-- 登入跨服返回
function M2G_LOGIN_MIDDLE_RET(fd, msg)
	if Config.IS_MIDDLE == true then return end
	if msg.svrIndex ~= Config.SVR_INDEX then 
		assert(nil, " svr index not equip")
		return 
	end
		
	local human = Human.onlineUuid[msg.uuid]
	if human == nil then return end
	
	local msgRet = Msg.gc.GC_MIDDLE_ENTER
	msgRet.result = msg.retCode
	msgRet.tip = MiddleDefine.G2M_TIP[msg.retCode]
	msgRet.act = 0
	msgRet.copyID = 0
	Msg.send(msgRet, human.fd)

	if msg.retCode ~= MiddleDefine.G2M_LOGIN_OK then return end

	human.db.middleFlag = 1
--	human.db.svrIndex = Config.SVR_INDEX
	Log.write(Log.LOGID_OSS_MIDDLE, Log.makeHumanInfo(human), LogDefine.MIDDLE_ACTION.SET_MIDDLE_FLAG_1) 
	Human.disconnect(human, CommonDefine.DISCONNECT_G2M, CommonDefine.DISCONNECT_MSG[CommonDefine.DISCONNECT_G2M])
	
	local family 
	if human.familyUuid ~= "" then
		family = FamilyManager.findByUuid(human.familyUuid)
		if family == nil then
			human.familyUuid = ""
			human.db.familyUuid = ""
		end
	end
	
	-- 向跨服服发送G2M_LOGIN_MIDDLE_DATA
	_G.msg_middle_parse.writebegin(0)
	_G.msg_middle_parse.writeint(MPacketDefine.G2M_LOGIN_MIDDLE_DATA)
	_G.msg_middle_parse.writeint(Config.SVR_INDEX)
  local db = RoleFFI.get_default_db()
  RoleFFI.copy_ffi_to_db(human.db, db)
  db.svrIndex = Config.SVR_INDEX
  db.name = "[" .. human.db.svrIndex%100000 .. "]" .. db.name
	_G.msg_middle_parse.writetable(db)
	if family then
		_G.msg_middle_parse.writestring(family.familyName)
		_G.msg_middle_parse.writeint(family.familyLv)
	else
		_G.msg_middle_parse.writestring("")
		_G.msg_middle_parse.writeint(0)
	end
	_G.msg_middle_parse.writeend()
	
	print("send GC_MIDDLE_ENTER 2 human:", human._id)
end

-- 登入跨服数据传递
function G2M_LOGIN_MIDDLE_DATA(fd, msg)
	if not Config.IS_MIDDLE then return end
	
	local svrIndex = nil 
	local svrInfo = FD_2_SVNINDEX[fd]
	if svrInfo then svrIndex = svrInfo.svrIndex end
	if svrIndex == nil then return end
	
	if svrIndex ~= msg.svrIndex then
		assert(nil, "G2M_LOGIN_MIDDLE_DATA svrIndex err")
	end
	
	local uuid = msg.db._id
	if uuid == nil or uuid == "" then return end
  -- 数据保存入db
  local db =  msg.db
--  db.name = "[" .. svrIndex%100000 .. "]" .. db.name
    
  -- 跨服里面家族相关数据
  if db.familyUuid then
      db.familyUuidM = db.familyUuid
      db.familyNameM = msg.familyName
      db.familyLvM = msg.familyLv
  else
      db.familyUuidM = ""
      db.familyNameM = ""
      db.familyLvM = 0
  end
    
  db.familyUuid = nil
  local hmapID = Map.getHomeMapID()
  local x, y = Map.getHomePoint(hmapID)
  db.map_id = hmapID
  db.x = x
  db.y = y
  
  local human = Human.onlineUuid[uuid]
	if human then
	  if human.fd then
		  assert(nil, "G2M_LOGIN_MIDDLE_DATA err user online" .. uuid)
		end
		-- 先登出跨服，再马上登陆跨服，2分钟内可能会跑到这里
		RoleFFI.copy2ffi(human.db, db)
		Human.save(human)
	else		
		
		local ret, data = RoleDBLogic.isUuidExistInDB(uuid) 
		if data then
			-- _id已存在 update
			if data.account ~= db.account then
				assert(nil, " login g2m but account not equal:" .. db.account .. " " .. data.account)
			end
			RoleDBLogic.saveRole(db)
		else
			-- _id不存在 insert
			LuaMongo.insert(DB.db_char, db)
		end
	end
	print("G2M_LOGIN_MIDDLE_DATA:", db.account, db.middleFlag)
end

-- 登出跨服请求
function M2G_LOGOUT_MIDDLE(fd, msg)
	if Config.IS_MIDDLE == true then return end
	
	local uuid = msg.db._id
	if uuid == nil or uuid == "" then return end

	local retCode = MiddleDefine.M2G_LOGIN_OK
	if Human.onlineUuid[uuid] and Human.onlineUuid[uuid].fd then
		retCode = MiddleDefine.M2G_LOGIN_FAIL_HAS_ACCOUNT
	elseif msg.svrIndex ~= Config.SVR_INDEX then
		retCode = MiddleDefine.M2G_LOGIN_FAIL_INVALID
	end
	
	_G.msg_middle_parse.writebegin(0)
	_G.msg_middle_parse.writeint(MPacketDefine.G2M_LOGOUT_MIDDLE_RET)
	_G.msg_middle_parse.writeint(Config.SVR_INDEX)
	_G.msg_middle_parse.writeint(retCode)
	_G.msg_middle_parse.writestring(msg.db._id)
	_G.msg_middle_parse.writeend()
end

-- 登出跨服返回
function G2M_LOGOUT_MIDDLE_RET(fd, msg)
	if not Config.IS_MIDDLE then return end
	
	local svrIndex = nil 
	local svrInfo = FD_2_SVNINDEX[fd]
	if svrInfo then svrIndex = svrInfo.svrIndex end
	if svrIndex ~= msg.svrIndex then
		assert(nil, "G2M_LOGOUT_MIDDLE_RET svrIndex err")
	end
	
	local human = Human.onlineUuid[msg.uuid]
	if human == nil then return end
	
	local msgRet = Msg.gc.GC_MIDDLE_LEAVE
	msgRet.result = msg.retCode
	msgRet.tip = MiddleDefine.M2G_TIP[msg.retCode]
	Msg.send(msgRet, human.fd)
	
	if msg.retCode ~= MiddleDefine.M2G_LOGIN_OK then return end
	
	Human.disconnect(human, CommonDefine.DISCONNECT_M2G, CommonDefine.DISCONNECT_MSG[CommonDefine.DISCONNECT_M2G])
	
	print("send GC_MIDDLE_LEAVE 2 human:", human.db._id,msg.retCode)
end

-- 登出跨服游戏数据传递
function M2G_LOGOUT_MIDDLE_DATA(fd, msg)
	if Config.IS_MIDDLE == true then return end
		
	if msg.svrIndex ~= Config.SVR_INDEX then
		assert(nil, "M2G_LOGOUT_MIDDLE_DATA svrIndex err")
	end
		
	local uuid = msg.db._id
	if uuid == nil or uuid == "" then return end

  local db =  msg.db
  local hmapID = Map.getHomeMapID()
  local x, y = Map.getHomePoint(hmapID)
  db.map_id = hmapID
  db.x = x
  db.y = y
  -- 跨服里面家族相关数据要清空
  db.familyUuidM = ""
  db.familyNameM = ""
  db.familyLvM = 0
  db.middleFlag = 0
  
  local human = Human.onlineUuid[uuid]
	if human then
	  if human.fd == nil then  -- 去跨服马上回来了。这里用跨服的覆盖
	     db.name = human.name
	     db.familyUuid = human.familyUuid
	     db.svrIndex = human.db.svrIndex
	     db.map_id = human.db.map_id
	     db.x = human.db.x
	     db.y = human.db.y
	     db.offlinePkRank = human.db.offlinePkRank
	     RoleFFI.copy2ffi(human.db, db)
	     Human.save(human)
	     print("M2G_LOGOUT_MIDDLE_DATA online:", human._id)
	  end
	else
		-- 数据保存入db				
		local ret, data = RoleDBLogic.isUuidExistInDB(uuid) 
		if data and data.middleFlag == 1 then
			-- _id已存在 update
			if data.account ~= db.account then
				assert(nil, " login g2m but account not equal:" .. db.account .. " " .. data.account)
			end
			if data.middleFlag ~= 1 then
				assert(nil, " login g2m but data.middlefalg ~= 1:" .. db.account .. " " .. data.account)
			end
		
			db.name = data.name
			db.familyUuid = data.familyUuid
			db.svrIndex = data.svrIndex
			db.map_id = data.map_id
      db.x = data.x
      db.y = data.y
      db.offlinePkRank = data.offlinePkRank
			RoleDBLogic.saveRole(db)
			
			print("M2G_LOGOUT_MIDDLE_DATA db middle flag set nil account:", data.account)
			Log.write(Log.LOGID_OSS_MIDDLE, uuid, db.account, db.name, LogDefine.MIDDLE_ACTION.SET_MIDDLE_FLAG_0)
		elseif not data then
			-- _id不存在 insert
			-- LuaMongo.insert(DB.db_char, db)
			assert(nil, " imposible for db no this data")
		end
	end	
	_G.msg_middle_parse.writebegin(0)
	_G.msg_middle_parse.writeint(MPacketDefine.G2M_LOGOUT_MIDDLE_DATA_RET)
	_G.msg_middle_parse.writestring(uuid)
	_G.msg_middle_parse.writeend()	
end

-- 登出跨服游戏数据确认
function G2M_LOGOUT_MIDDLE_DATA_RET(fd, msg)
	local uuid = msg.uuid 
	if uuid == nil or uuid == "" then return end
	
	local h = Human.onlineUuid[uuid] 
	if h then
	  h.db.middleFlag = 0
		Human.disconnect(h, CommonDefine.DISCONNECT_M2G_ERR, CommonDefine.DISCONNECT_MSG[CommonDefine.DISCONNECT_M2G_ERR])
	end
	
	local targetDB = RoleDBLogic.getDbByUuid(uuid, fields)
	if targetDB == nil then
		assert(nil, "G2M_LOGOUT_MIDDLE_DATA_RET db == nil")
	end
	targetDB.middleFlag = 0
	RoleDBLogic.saveRoleSset(targetDB)
	
	Log.write(Log.LOGID_OSS_MIDDLE, uuid, targetDB.account, targetDB.name, LogDefine.MIDDLE_ACTION.SET_MIDDLE_FLAG_0)
end

function enter(human, actID, teamCopyID)
	if Config.IS_MIDDLE == true then return end
	local retCode
	local tip
	local flag
	if Config.SVR_INDEX == 3 then
		retCode = MiddleDefine.G2M_LOGIN_FAIL_FENGCE
		tip = MiddleDefine.G2M_TIP[retCode]		
	elseif _G.msg_middle_parse.check_connect() ~= true then
		retCode = MiddleDefine.G2M_LOGIN_FAIL_WEIHU
		tip = MiddleDefine.G2M_TIP[retCode]
	elseif ENABLE_MIDDLE == false then
		retCode = MiddleDefine.G2M_LOGIN_FAIL_WEIHU
		tip = MiddleDefine.G2M_TIP[retCode]
  elseif not MiddleOpenDB.isMiddleOpen() then
    retCode = MiddleDefine.G2M_LOGIN_FAIL_WEIHU
    tip = MiddleDefine.G2M_TIP[retCode]
	elseif human.db.lv < 30 then
		retCode = MiddleDefine.G2M_LOGIN_FAIL_LV
		tip = MiddleDefine.G2M_TIP[retCode]
  elseif actID ~= "" then
    if MiddleActivity.checkOpenMiddle(actID) == 0 then
        retCode =  MiddleDefine.G2M_LOGIN_FAIL_ACT_ERR
        tip = Lang.MIDDLE_ACT_NO_OPEN_MIDDLE
    else
        local err = MiddleActivity.checkCanEnterMiddle(human, actID, teamCopyID)
        if err then
            retCode = MiddleDefine.G2M_LOGIN_FAIL_ACT_ERR
            tip = err
            flag = true     
        end
    end
  elseif teamCopyID == CopyDefine.COPY_FAMILYWAR_ID then
    local err = MiddleFamilySign.checkCanEnterMiddle(human)
    if err then
            retCode = MiddleDefine.G2M_LOGIN_FAIL_ACT_ERR
            tip = err
            flag = true    
    end
	end
	
	if retCode then
		local msgRet = Msg.gc.GC_MIDDLE_ENTER
		msgRet.result = retCode
		msgRet.tip = tip
		msgRet.act = flag and 0 or 1
		msgRet.copyID = teamCopyID
		Msg.send(msgRet, human.fd)
		return
	end

	local db = RoleFFI.get_default_db()
  RoleFFI.copy_ffi_to_db(human.db, db)

	-- 向跨服服发送G2M_LOGIN_MIDDLE
	_G.msg_middle_parse.writebegin(0)
	_G.msg_middle_parse.writeint(MPacketDefine.G2M_LOGIN_MIDDLE)
	_G.msg_middle_parse.writeint(Config.SVR_INDEX)
	_G.msg_middle_parse.writetable(db)
	_G.msg_middle_parse.writestring(actID)
	_G.msg_middle_parse.writeint(teamCopyID)
	_G.msg_middle_parse.writeend()
	
	print("actID:", actID, teamCopyID)
	return true
end

function leave(human)
	if not Config.IS_MIDDLE then return end
		
	local fd 
	local svrIndex
	
	for fdTemp, svrTempInfo in pairs(FD_2_SVNINDEX) do
		if svrTempInfo.svrIndex == human.db.svrIndex then
			fd = fdTemp
			svrIndex = svrTempInfo.svrIndex
			break
		end
	end
	
	if svrIndex == nil then 
		assert(nil, "why svrIndex == nil " .. human.name .. " svrIndex:" .. human.db.svrIndex)
	end
	if svrIndex ~= human.db.svrIndex then 
		assert(nil, "want leave middle, but svrindex err")
	end
	
	local db = RoleFFI.get_default_db()
  RoleFFI.copy_ffi_to_db(human.db, db)
    
	-- 向正常服发送M2G_LOGOUT_MIDDLE
	_G.msg_middle_parse.writebegin(fd)
	_G.msg_middle_parse.writeint(MPacketDefine.M2G_LOGOUT_MIDDLE)
	_G.msg_middle_parse.writeint(svrIndex)
	_G.msg_middle_parse.writetable(db)
	_G.msg_middle_parse.writeend()	
end



function M2G_LOGIN_MIDDLE_TIMEOUT(fd, msg)
	if Config.IS_MIDDLE == true then return end

	local uuid = msg.uuid
	if uuid == nil or uuid == "" then return end
	if Human.onlineUuid[uuid] ~= nil then return end
	
	local targetDB = RoleDBLogic.getDb(uuid, fields)
	if targetDB == nil then
		assert(nil, "M2G_LOGIN_MIDDLE_TIMEOUT db == nil")
	end
	targetDB.middleFlag = 0
	RoleDBLogic.saveRoleSset(targetDB)	
	print("time out set middleFlag 2 0")
	Log.write(Log.LOGID_OSS_MIDDLE, uuid, targetDB.account, targetDB.name, LogDefine.MIDDLE_ACTION.SET_MIDDLE_FLAG_0_TIMEOUT)
end

function heartBeat()
	if not Config.IS_MIDDLE then
		-- 游戏服每两分钟向跨服服发一次心跳
		if _G.msg_middle_parse.check_connect() == true then
			--	print("send heartBeat packet")
			_G.msg_middle_parse.writebegin(0)
			_G.msg_middle_parse.writeint(MPacketDefine.G2M_HEARTBEAT)
			_G.msg_middle_parse.writeint(10)
			_G.msg_middle_parse.writeint(1)		
			_G.msg_middle_parse.writeend()
		end
	else
		-- 跨服服每两分钟检查一次待登录帐号里面的过期数据
		local now = os.time()
		for uuid, loginData in pairs(NEED_LOGIN_DATA) do
			if now - loginData.time > 120 then
				local fd = getFDBySvrIndex(loginData.svrIndex)
				if fd then
					_G.msg_middle_parse.writebegin(fd)
					_G.msg_middle_parse.writeint(MPacketDefine.M2G_LOGIN_MIDDLE_TIMEOUT)
					_G.msg_middle_parse.writestring(uuid)
					_G.msg_middle_parse.writeend()
					
					local targetDB = RoleDBLogic.getDbByUuid(uuid, fields)
					if targetDB then
						targetDB.middleFlag = 0
						RoleDBLogic.saveRoleSset(targetDB)
					end
		
					NEED_LOGIN_DATA[uuid] = nil
				end
			end
		end
	end
end

function G2M_HEARTBEAT(fd, msg)
	local info = FD_2_SVNINDEX[fd]
	if info == nil then return end
	local openDay = msg.openDay
	if openDay < 1 then
		openDay = 1
	end
	info.openDay = openDay
	info.avgZhandouli = msg.avgZhandouli
end

function middleLogin(human, actID, copyID)
  if not Config.IS_MIDDLE then return true end
  if actID ~= "" then --跨服活动
      local err = MiddleActivity.checkCanEnterMiddle(human, actID, copyID)
      if err then
          return
      end
  else   --跨服副本
      local err 
      if copyID == CopyDefine.COPY_FAMILYWAR_ID then
          err = MiddleFamilySign.checkCanEnterMiddle(human)
          if err then
              return
          end
      else
          err = MiddleCopySign.checkCanEnterMiddle(human, copyID)
          if err then
              return
          end
      end
  end  
  return true
end

function middleGM(gm, arg)
  if _G.msg_middle_parse.check_connect() == true then
      _G.msg_middle_parse.writebegin(0)
      _G.msg_middle_parse.writeint(MPacketDefine.G2M_LOGIN_MIDDLE_GM)
      _G.msg_middle_parse.writestring(gm)
      _G.msg_middle_parse.writestring(arg)   
      _G.msg_middle_parse.writeend()
      print("send2middleGM:", gm, arg)
  end
end
function G2M_LOGIN_MIDDLE_GM(fd, msg)
  if not Config.IS_MIDDLE then return end
  if msg.gmcmd == "middlestartact" then
      local nData = {
        type = 5,
        data = {actType = tonumber(msg.arg)}
      }
      require("activity.HandlerW").L2WProcess(nil, nil, nData)
  elseif msg.gmcmd == "middlecloseact" then
      local nData = {
        type = 6,
        data = {actType = tonumber(msg.arg)}
      }
      require("activity.HandlerW").L2WProcess(nil, nil, nData)
  elseif msg.gmcmd == "middlefamilywar" then
      MiddleFamilySign.gmCheckTime(msg.arg and tonumber(msg.arg) or nil)
  end
  print("G2M_LOGIN_MIDDLE_GM:",msg.gmcmd, msg.arg)
end

local ServerCommon = require("serverCommon.ServerCommonFW")


function checkNeedOpen()
  if PfLogic.isAgentTencent() then
    -- 开服的第2天要自动开跨服
    if MiddleOpenDB.isMiddleOpen() == true then return end
  
    local openDay = ServerCommon.getHadOpenDay()
    if openDay == 0 or openDay == 1 then return end

    MiddleOpenDB.setMiddleOpen(true)
  end
end

local field = {svrIndex = 1}
function sendMail(uuid, title, content, gridlist)
  if Config.IS_MIDDLE then
      local db = RoleDBLogic.getDb(uuid, field)
      if db then
          local fd = getFDBySvrIndex(db.svrIndex)
          if fd then
              _G.msg_middle_parse.writebegin(fd)
              _G.msg_middle_parse.writeint(MPacketDefine.M2G_SEND_MAIL)
              _G.msg_middle_parse.writestring(uuid)
              _G.msg_middle_parse.writestring(title)
              _G.msg_middle_parse.writestring(content)
              _G.msg_middle_parse.writetable(gridlist or {})          
              _G.msg_middle_parse.writeend()
          end
      end
  else
      MailLogicFW.addSystemMessage(uuid, title,content, gridlist)       
  end
end
